package com.code.common.utils.secure;

import cn.hutool.core.codec.Base64;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.interfaces.ECPrivateKey;
import org.bouncycastle.jce.interfaces.ECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.provider.JCEECPublicKey;
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
import org.springframework.util.Assert;

import javax.crypto.KeyAgreement;
import java.security.*;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.PKCS8EncodedKeySpec;

/**
 * Created by IntelliJ IDEA.
 *
 * @author: Yan
 * @date: 2023/03/31
 */
@Slf4j
public class EcdhUtil {

    private static final String STD_NAME = "prime256v1";
    private static final String PROVIDER = "BC";

    private KeyPairGenerator keyPairGenerator;
    private ECPrivateKey ecPrivateKey;
    private ECPublicKey ecPublicKey;

    public EcdhUtil() {
        try {
            Security.addProvider(new BouncyCastleProvider());
            keyPairGenerator = KeyPairGenerator.getInstance("ECDH", PROVIDER);
            keyPairGenerator.initialize(new ECGenParameterSpec(STD_NAME));
            KeyPair keyPair = keyPairGenerator.generateKeyPair();
            ecPrivateKey = (ECPrivateKey) keyPair.getPrivate();
            ecPublicKey = (ECPublicKey) keyPair.getPublic();
        }catch (NoSuchProviderException | NoSuchAlgorithmException | InvalidAlgorithmParameterException e) {
            e.printStackTrace();
        }
    }

    private PublicKey getPublicKey() {
        return ecPublicKey;
    }

    private PrivateKey getPrivateKey() {
        return ecPrivateKey;
    }

    private KeyPairGenerator getKeyPairGenerator() {
        return keyPairGenerator;
    }

//    /**
//     * 解析前端字符串公钥
//     * @param publicKey base64字符串公钥
//     * @return
//     */
//    private org.bouncycastle.jce.interfaces.ECPublicKey decodePublicKey(String publicKey){
//        Assert.notNull(publicKey, "ECDH base64 public key is not null.");
//        try {
//            X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKey));
//            KeyFactory keyFactory = KeyFactory.getInstance(this.getKeyPairGenerator().getAlgorithm(), "BC");
//            return  ((org.bouncycastle.jce.interfaces.ECPublicKey) keyFactory.generatePublic(x509EncodedKeySpec));
//        } catch (Exception e){
//            e.printStackTrace();
//            return null;
//        }
//    }

    /**
     * 解析前端字符串公钥
     * @param encodedPublicKey base64字符串公钥
     * @return
     */
    private PublicKey decodePublicKey(String encodedPublicKey) {
        Assert.notNull(encodedPublicKey, "ECDH base64 public key is not null.");
        byte[] decodedPublicKey = Base64.decode(encodedPublicKey);

        ECNamedCurveParameterSpec parameterSpec = ECNamedCurveTable.getParameterSpec(STD_NAME);

        org.bouncycastle.jce.spec.ECPublicKeySpec ecPublicKeySpec = new org.bouncycastle.jce.spec.ECPublicKeySpec(
                parameterSpec.getCurve().decodePoint(decodedPublicKey),
                parameterSpec
        );
        ECPublicKey ecPublicKey = new JCEECPublicKey(this.getKeyPairGenerator().getAlgorithm(), ecPublicKeySpec);

        return ecPublicKey;
    }

    /**
     * 解释服务器端的私钥
     * @param privateKey base64字符串私钥
     * @return
     */
    public PrivateKey decodePrivateKey(String privateKey){
        Assert.notNull(privateKey, "ECDH base64 private key is not null.");
        try {
            PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decode(privateKey));
            KeyFactory keyFactory = KeyFactory.getInstance(this.getKeyPairGenerator().getAlgorithm(), PROVIDER);
            return keyFactory.generatePrivate(pkcs8EncodedKeySpec);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 磋商产生密钥
     * @param privateKey 服务端的私钥
     * @param publicKey 客户端传过来的公钥
     * @return 返回磋商后的base64密钥
     */
    public String computedSecretDP(PrivateKey privateKey, String publicKey) {
        try {
            ECPublicKey ecPublicKey = (ECPublicKey) this.decodePublicKey(publicKey);
            // 磋商密钥
            KeyAgreement agreement = KeyAgreement.getInstance(this.getKeyPairGenerator().getAlgorithm());
            // 私钥
            agreement.init(privateKey);
            // 传过来的公钥
            agreement.doPhase(ecPublicKey, true);

            return Base64.encode(agreement.generateSecret());
        }catch (NoSuchAlgorithmException | InvalidKeyException e) {
            e.printStackTrace();
        }
        return null;
    }

    public String computedSecret(String publicKey) {
        return this.computedSecretDP(this.getPrivateKey(), publicKey);
    }

    public String getPrivateKeyBase64() {
        return Base64.encode(this.getPrivateKey().getEncoded());
    }

    public String getPublicKeyBase64() {
        byte[] encodedKey = ((ECPublicKey) this.getPublicKey()).getQ().getEncoded(true);
        return java.util.Base64.getEncoder().encodeToString(encodedKey);
    }

}
